﻿using ClusteringProblem.Application.Helpers;
using ClusteringProblem.Presenter;
using ClusteringProblem.View;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;

namespace ClusteringProblem.Application
{
    public partial class MainWindow : Form, IMainWindowView
    {
        private MainWindowPresenter presenter;
        public delegate void AddDataDelegate();
        public AddDataDelegate addDataDel;        
        private CancellationTokenSource cancelToken = new CancellationTokenSource();
        private int iteration;

        public MainWindow()
        {
            InitializeComponent();
            presenter = new MainWindowPresenter(this);
            iteration = 0;
        }

        private void MainWindow_Load(object sender, EventArgs e)
        {
            SetDistanceMeasureCombobox();
            SetInitialAlgorithmRadiobutton();
            addDataDel += new AddDataDelegate(AddData);
            chart1.EnableZoomAndPanControls(null, ChartCursorMoved);
            chart1.Visible = false;
            btn_SaveChart.Enabled = false;
        }
        private void SetDistanceMeasureCombobox()
        {
            this.cbo_DistanceMeasure.DataSource = new BindingSource(EnumsExtension.DistanceMeasureToDictionary(), null);
            this.cbo_DistanceMeasure.DisplayMember = "Key";
            this.cbo_DistanceMeasure.ValueMember = "Value";
        }

        private void SetInitialAlgorithmRadiobutton()
        {
            rb_NACRE.Select();
        }

        public MainWindowPresenter Presenter
        {
            get { return presenter; }
            set { presenter = value; }
        }

        public bool StartEnabled
        {
            set { btn_Start.Enabled = value; }
        }

        public string DatasetPath
        {
            get { return openFileDialog1.FileName; }
        }

        public string DatasetName
        {
            set { txt_DatasetName.Text = value; }
            get { return txt_DatasetName.Text; }
        }

        public string AttributesCount
        {
            set { txt_AttributesCount.Text = value; }
        }

        public string ObjectsCount
        {
            set { txt_ObjectsCount.Text = value; }
        }

        public byte[] Image
        {
            set
            {                
                MemoryStream ms = new MemoryStream(value);
                Image img = System.Drawing.Image.FromStream(ms);                
                pic_Dataset.Image = img;
            }
        }

        public string GroupsCount
        {
            set { txt_GroupsCount.Text = value; }
        }

        public string Description
        {
            set { txt_Description.Text = value; }
        }

        public string Distance
        {
            get 
            {
                if (this.InvokeRequired)
                {
                    return (string) this.Invoke(new Func<string>(this.GetDistanceProp));
                }

                return this.GetDistanceProp();                
            }                 
        }

        private string GetDistanceProp()
        {
            return cbo_DistanceMeasure.SelectedValue.ToString();
        }

        public double RandCoefficientValue
        {            
            set
            {
                if (this.InvokeRequired)
                {
                    this.Invoke(new Action<string>(this.SetRandCoefficientProp), value.ToString("F6"));
                    return;
                }

                this.SetRandCoefficientProp(value.ToString("F6"));            
            }
        }

        public double FMeasureValue
        {
            set 
            {
                if (this.InvokeRequired)
                {
                    this.Invoke(new Action<string>(this.SetFmeasureProp), value.ToString("F6"));
                    return;
                }

                this.SetFmeasureProp(value.ToString("F6")); 
            }
        }

        public double ClassificationErrorValue
        {
            set 
            {
                if (this.InvokeRequired)
                {
                    this.Invoke(new Action<string>(this.SetClassificationErrorProp), value.ToString("F6"));
                    return;
                }

                this.SetClassificationErrorProp(value.ToString("F6")); 
            }
        }

        private void SetRandCoefficientProp(string value)
        {
            txt_RandCoefficientValue.Text = value;
        }

        private void SetFmeasureProp(string value)
        {
            txt_FmeasureValue.Text = value;
        }

        private void SetClassificationErrorProp(string value)
        {
            txt_ClassificationErrorValue.Text = value;
        }

        List<Model.Object> IMainWindowView.DataSource
        {
            set
            {
                if (this.InvokeRequired)
                {
                    this.Invoke(new Action<List<Model.Object>>(this.SetDataSourceProp), value);
                    return;
                }

                this.SetDataSourceProp(value);
            }
        }

        private void SetDataSourceProp(List<Model.Object> source)
        {
            dgv_SourceData.DataSource = source;
            dgv_SourceData.Columns[0].HeaderText = "ID Obiektu";
            dgv_SourceData.Columns[1].HeaderText = "ID Grupy";
            dgv_SourceData.Columns[2].HeaderText = "Wektor cech";
        }        

        public List<Model.IAntType> DataResult
        {
            set
            {
                if (this.InvokeRequired)
                {
                    this.Invoke(new Action<List<Model.IAntType>>(this.SetResultSourceProp), value);
                    return;
                }

                this.SetResultSourceProp(value);
            }
        }

        private void SetResultSourceProp(List<Model.IAntType> result)
        {
            dgv_ResultData.DataSource = result;
            dgv_ResultData.Columns[0].HeaderText = "ID Obiektu";
            dgv_ResultData.Columns[1].HeaderText = "ID Grupy";
        }

        #region MSChartExtension
                
        private void ChartCursorMoved(double x, double y)
        {
            lbl_ChartValue.Text = x.ToString("F4") + ", " + y.ToString("F4");
        }

        #endregion

        public bool LoadDatasetFlag
        {
            set { presenter.LoadDatasetFlag = value; }
        }

        public bool IsNormalizeData
        {
            get { return cbx_NormalizeData.Checked; }
        }

        private void btn_Start_Click(object sender, EventArgs e)
        {            
            tabControl1.SelectTab(ResultTabPage);
            txt_RandCoefficientValue.Text = "";
            txt_FmeasureValue.Text = "";
            txt_ClassificationErrorValue.Text = "";
            dgv_SourceData.DataSource = null;
            dgv_ResultData.DataSource = null;


            if (this.rb_ModNACRE.Checked)
            {
                chart1.Titles.Add("Wykres zmiany wartości parametru sigma");
                chart1.Titles[0].Font = new System.Drawing.Font("Candara", 14F, System.Drawing.FontStyle.Bold);

                chart1.ChartAreas[0].AxisX.Minimum = 0;
                chart1.ChartAreas[0].AxisX.Maximum = Convert.ToDouble(txt_iterationCountModNACRE.Text);
                chart1.ChartAreas[0].AxisX.Title = "Iteracja";

                chart1.Series.Clear();

                Series newSeries = new Series("Sigma");
                newSeries.ChartType = SeriesChartType.Line;
                newSeries.BorderWidth = 2;
                newSeries.Color = Color.OrangeRed;
                newSeries.XValueType = ChartValueType.Int32;
                chart1.Series.Add(newSeries);

                cancelToken.Dispose();
                cancelToken = new CancellationTokenSource();

                Task.Factory.StartNew(() =>
                {
                    while (true)
                    {
                        chart1.Invoke(addDataDel);
                        if (txt_SigmaValue.InvokeRequired)
                        {
                            txt_SigmaValue.Invoke((MethodInvoker)delegate { txt_SigmaValue.Text = Sigma.ToString("F16"); });
                        }

                        System.Threading.Thread.Sleep(50);

                        if (cancelToken.Token.IsCancellationRequested)
                        {
                            if (btn_SaveChart.InvokeRequired)
                            {
                                btn_SaveChart.Invoke((MethodInvoker)delegate { btn_SaveChart.Enabled = true; });
                            }
                            break;
                        }
                    }

                }, cancelToken.Token);
            }
            
            Task.Factory.StartNew(() => presenter.RunAlgorithm());
                        
        }
                
        private void btn_LoadDataSet_Click(object sender, EventArgs e)
        {
            openFileDialog1.Filter = "Data sets file (.data)|*.data";
            openFileDialog1.FileName = null;
            // ustawienie początkowej ścieźki na ścieźkę projektu
            string appPath = Path.GetDirectoryName(
                Path.GetDirectoryName(System.IO.Directory.GetCurrentDirectory()));
            string initialPath = appPath.Substring(0, appPath.LastIndexOf(@"\")) + @"\ClusteringProblem.Database\Datasets";
            openFileDialog1.InitialDirectory = initialPath;
            
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                string name = Path.GetFileNameWithoutExtension(openFileDialog1.FileName);
                DatasetName = name;
                lbl_LoadMsg.ForeColor = System.Drawing.Color.Green;
                lbl_LoadMsg.Text = string.Format("Zakończono wczytywanie zbioru danych: {0}", name);
                this.LoadDatasetFlag = true;
                presenter.SetDatasetInfo();
            }            
        }

        private void rb_NACRE_CheckedChanged(object sender, EventArgs e)
        {
            errorProvider1.Clear();
            errorProvider2.Clear();
            txt_iterationCount1.Enabled = rb_NACRE.Checked;
            txt_sigma.Enabled = rb_NACRE.Checked;
            presenter.IsNACRE = rb_NACRE.Checked;
        }

        private void rb_ModNACRE_CheckedChanged(object sender, EventArgs e)
        {
            errorProvider1.Clear();
            errorProvider2.Clear();
            txt_iterationCountModNACRE.Enabled = rb_ModNACRE.Checked;
            presenter.IsModifiedNACRE = rb_ModNACRE.Checked;
        }

        private void rb_DBSCAN_CheckedChanged(object sender, EventArgs e)
        {
            errorProvider1.Clear();
            errorProvider2.Clear();
            txt_epsilon.Enabled = rb_DBSCAN.Checked;
            txt_minPts.Enabled = rb_DBSCAN.Checked;
            presenter.IsDBSCAN = rb_DBSCAN.Checked;
        }

        private void rb_KMeans_CheckedChanged(object sender, EventArgs e)
        {
            errorProvider1.Clear();
            errorProvider2.Clear();
            txt_iterationCount2.Enabled = rb_KMeans.Checked;
            txt_groupCount.Enabled = rb_KMeans.Checked;
            presenter.IsKMeans = rb_KMeans.Checked;
        }

        private void txt_iterationCount1_TextChanged(object sender, EventArgs e)
        {
            errorProvider1.Clear();
            presenter.IterationCount = ParseAsInt(txt_iterationCount1, txt_iterationCount1.Text);
        }

        private void txt_iterationCountModNACRE_TextChanged(object sender, EventArgs e)
        {
            errorProvider1.Clear();
            presenter.IterationCount = ParseAsInt(txt_iterationCountModNACRE, txt_iterationCountModNACRE.Text);
        }  

        private void txt_sigma_TextChanged(object sender, EventArgs e)
        {
            errorProvider2.Clear();
            presenter.Sigma = ParseAsDouble(txt_sigma, txt_sigma.Text);
        }

        private void txt_epsilon_TextChanged(object sender, EventArgs e)
        {
            errorProvider1.Clear();
            presenter.Epsilon = ParseAsDouble(txt_epsilon, txt_epsilon.Text);
        }

        private void txt_minPts_TextChanged(object sender, EventArgs e)
        {
            errorProvider2.Clear();
            presenter.MinPts = ParseAsInt(txt_minPts, txt_minPts.Text);
        }

        private void txt_iterationCount2_TextChanged(object sender, EventArgs e)
        {
            errorProvider1.Clear();
            presenter.IterationCount = ParseAsInt(txt_iterationCount2, txt_iterationCount2.Text);
        }

        private void txt_groupCount_TextChanged(object sender, EventArgs e)
        {
            errorProvider2.Clear();
            presenter.GroupsCount = ParseAsInt(txt_groupCount, txt_groupCount.Text);
        }

        private double ParseAsDouble(Control control, string p)
        {
            errorProvider2.Clear();
            try
            {
                return Double.Parse(p);
            }
            catch (Exception)
            {
                errorProvider2.SetError(control, "Nie poprawna wartość !!!");
                return 0.0;
            }
        }

        private int ParseAsInt(Control control, string p)
        {
            errorProvider1.Clear();
            try
            {
                return Int32.Parse(p);
            }
            catch (Exception)
            {
                errorProvider1.SetError(control, "Nie poprawna wartość !!!");
                return 0;
            }
        }               

        public double Sigma
        {
            get;
            set;
        }
        
        private void AddData()
        {
            DateTime timeStamp = DateTime.Now;
            
            foreach (Series ptSeries in chart1.Series)
            {
                iteration += 10;
                AddNewPoint(iteration, ptSeries);
            }
        }

        private void AddNewPoint(int i, Series ptSeries)
        { 
            // Add new data point to its series.
            ptSeries.Points.AddXY(i, Sigma);
                        
            chart1.ChartAreas[0].AxisX.Minimum = 0;
            chart1.ChartAreas[0].AxisX.Maximum = Convert.ToDouble(txt_iterationCountModNACRE.Text);
                        
            chart1.Invalidate();
        }


        public CancellationTokenSource CancellationTokenSource
        {
            set { cancelToken = value; }
            get { return cancelToken; }
        }


        /// <summary>
        /// Zapis wykresu do jednego z formatów graficznych
        /// </summary>
        private void btn_SaveChart_Click(object sender, EventArgs e)
        {
            saveFileDialog1.InitialDirectory = @"C:\";
            saveFileDialog1.Title = "Zapisz plik graficzny";
                        
            saveFileDialog1.Filter = "BMP (*.bmp)|*.bmp|GIF (*.gif)|*.gif|JPEG (*.jpg)|*.jpg|PNG (*.png)|*.png|TIFF (*.tiff)|*.tif";
                        
            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                string path = Path.GetExtension(saveFileDialog1.FileName).Substring(1);

                switch (path)
                {
                    case "bmp":
                        chart1.SaveImage(saveFileDialog1.FileName, System.Windows.Forms.DataVisualization.Charting.ChartImageFormat.Bmp);
                    break;
                    case "gif":
                        chart1.SaveImage(saveFileDialog1.FileName, System.Windows.Forms.DataVisualization.Charting.ChartImageFormat.Gif);
                    break;
                    case "jpg":
                        chart1.SaveImage(saveFileDialog1.FileName, System.Windows.Forms.DataVisualization.Charting.ChartImageFormat.Jpeg);
                        break;
                    case "png":
                        chart1.SaveImage(saveFileDialog1.FileName, System.Windows.Forms.DataVisualization.Charting.ChartImageFormat.Png);
                        break;
                    case "tif":
                        chart1.SaveImage(saveFileDialog1.FileName, System.Windows.Forms.DataVisualization.Charting.ChartImageFormat.Tiff);
                        break;
                    default:
                        break;
                }

                saveFileDialog1.FileName = null;
                MessageBox.Show("Pomyślnie zakończono zapis wykresu do pliku graficznego.", "Komunikat");
            }
            
        }
        
        public bool IsSigmaChartVisible
        {
            set 
            { 
                if (this.InvokeRequired)
                {
                    this.Invoke(new Action<bool>(this.SetSigmaChartVisibleProp), value);
                    return;
                }

                this.SetSigmaChartVisibleProp(value); 
            }
        }

        private void SetSigmaChartVisibleProp(bool value)
        {
            chart1.Visible = value;
        }
    }
}
